﻿
using DataService.Core.Reflection;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.Serialization;
using System.Xml.Serialization;

namespace DataService.Core.Extensions
{
    public static class MemberInfoHelper
    {
       
        /// <summary>
        /// 根据属性名获取属性值
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="t">对象</param>
        /// <param name="name">属性名</param>
        /// <returns>属性的值</returns>
        public static object GetPropertyValue<T>(this T t, string name)
        {
            Type type = t.GetType();
            PropertyInfo p = type.GetProperty(name);
            if (p == null)
            {
                throw new Exception(String.Format("该类型没有名为{0}的属性", name));
            }
            object val = GetPropertyValue(t, p);
            return val;
        }

        /// <summary>
        /// 根据属性 获取属性值
        /// </summary>
        public static object GetPropertyValue<T>(this T t, PropertyInfo paramProertyinfo)
        {
            var param_obj = Expression.Parameter(typeof(T));
            var param_val = Expression.Parameter(typeof(object));

            // 非string类型会报转换object错误
            //  转成真实类型，防止Dynamic类型转换成object
            //var body_obj = Expression.Convert(param_obj, type);

            //var body = Expression.Property(body_obj, p);
            //var getValue = Expression.Lambda<Func<T, object>>(body, param_obj).Compile();

            //转成真实类型，防止Dynamic类型转换成object
            Expression<Func<T, object>> result = Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.Property(param_obj, paramProertyinfo), typeof(object)), param_obj);
            var getValue = result.Compile();
            return getValue(t);
        }

        /// <summary>
        /// 根据属性名称设置属性的值；注意 参数类型和值的类型需要对应，特别注意 64和32位值问题
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="t">对象</param>
        /// <param name="name">属性名</param>
        /// <param name="value">属性的值；如 string类型 传"测试"  int32类型 传 (int32)6  int64 传 (int64)100 等</param>
        public static void SetPropertyValue<T>(this T t, string name, object value)
        {
            Type type = t.GetType();
            PropertyInfo p = type.GetProperty(name);
            if (p == null)
            {
                throw new Exception(String.Format("该类型没有名为{0}的属性", name));
            }
            var param_obj = Expression.Parameter(type);
            var param_val = Expression.Parameter(typeof(object));
            var body_obj = Expression.Convert(param_obj, type);
            var body_val = Expression.Convert(param_val, p.PropertyType);

            //获取设置属性的值的方法
            var setMethod = p.GetSetMethod(true);

            //如果只是只读,则setMethod==null
            if (setMethod != null)
            {
                var body = Expression.Call(param_obj, p.GetSetMethod(), body_val);
                var setValue = Expression.Lambda<Action<T, object>>(body, param_obj, param_val).Compile();
                setValue(t, value);
            }
        }


        /// <summary>
        /// 返回排除部分字段属性
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ignoreExpression">排除的字段</param>
        /// <returns></returns>
        public static string[] GetFieldArrayIgnoreIn<T>(Expression<Func<T, object>> ignoreExpression)
        {
            PropertyInfo[] propertys = typeof(T).GetProperties();
            if (ignoreExpression == null)
            {
                return propertys.Select(x => x.Name).ToArray();
            }
            var accessList = ignoreExpression.Body.Type.GetProperties();
            List<string> fieldList = new List<string>();
            var tempList = accessList.Select(x => x.Name).ToList();
            var tempPropertyList = propertys.Where(x => !tempList.Contains(x.Name)).ToList();
            var tempFieldArray = tempPropertyList.Select(x => x.Name).ToArray();
            return tempFieldArray;
        }


        /// <summary>获取类型代码</summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static TypeCode GetTypeCode(this Type type) => Type.GetTypeCode(type);

        /// <summary>反射创建指定类型的实例</summary>
        /// <param name="type">类型</param>
        /// <param name="parameters">参数数组</param>
        /// <returns></returns>
        public static Object CreateInstance(this Type type, params Object[] parameters)
        {
            if (type == null) throw new ArgumentNullException(nameof(type));

            try
            {
                if (parameters == null || parameters.Length == 0)
                {
                    // 基元类型
                    return Activator.CreateInstance(type, true);
                }
                else
                {
                    return Activator.CreateInstance(type, parameters);
                }
            }
            catch (Exception ex)
            {
                throw new Exception($"创建对象失败 type={type.FullName} parameters={parameters} {ex.Message}");
            }
        }

        public static Object GetMemberValue(this Object target, MemberInfo member)
        {
            if (member == null)
            {
                member = target as MemberInfo;
                target = null;
            }
            if (member is PropertyInfo)
            {
                var property = member as PropertyInfo;
                return property.GetValue(target, null);
            }
            else if (member is FieldInfo)
            {
                var field = member as FieldInfo;
                return field.GetValue(target);
            }
            else
                throw new ArgumentOutOfRangeException(nameof(member));
        }
        public static void SetMemberValue(this Object target, MemberInfo member, Object value)
        {
            if (member is PropertyInfo)
            {
                var property = member as PropertyInfo;
                property.SetValue(target, value.ChangeType(property.PropertyType), null);
            }
            else if (member is FieldInfo)
            {
                var field = member as FieldInfo;
                field.SetValue(target, value.ChangeType(field.FieldType));
            }
            else
                throw new ArgumentOutOfRangeException(nameof(member));
        }




    }
}
